﻿using ManagementApi.Models.Auth;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using PmSoft;
using Microsoft.AspNetCore.Authentication;
using PmSoft.Exceptions;
using PmSoft.Entity;
using PmSoft.ApplicationContext;
using Modules.Corp;
using Modules.Constructor;
using Modules.Manager;
using Modules.Commander;
using Modules.System.User;
using Microsoft.Extensions.Caching.Memory;
using ManagementApi.Models.Account;
using AutoMapper;
using Modules.System.Security;
using Modules.Project;
using ManagementApi.Mvc.ApplicationContexts;

namespace ManagementApi.Services.Authentication
{
    /// <summary>
    /// 用户认证业务逻辑类
    /// </summary>
    [UseDI(ServiceLifetime.Scoped)]
    public class UserAuthenticationService
    {
        private readonly UserService userSevice;
        private readonly CorpService corpService;
        private readonly ManagerService managerService;
        private readonly ConstructorService constructorService;
        private readonly CommanderService commanderService;
        private readonly IHttpContextAccessor httpContextAccessor;
        private readonly ManagementApplicationContext applicationContext;
        private readonly IConfiguration configuration;
        private readonly IMemoryCache memoryCache;
        private readonly ILogger logger;
        private readonly IApplicationContext application;
        private readonly IMapper mapper;
        private readonly SecurityService securityService;
        private readonly CorpAttachmentService corpAttachmentService;
        private readonly ProjectService projectService;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="userSevice"></param>
        /// <param name="corpService"></param>
        /// <param name="managerService"></param>
        /// <param name="httpContextAccessor"></param>
        /// <param name="applicationContext"></param>
        /// <param name="configuration"></param>
        public UserAuthenticationService(UserService userSevice,
            CorpService corpService,
            ManagerService managerService,
            ConstructorService constructorService,
             CommanderService commanderService,
            IHttpContextAccessor httpContextAccessor,
            ManagementApplicationContext applicationContext,
            IConfiguration configuration,
            IMemoryCache memoryCache,
            ILogger<UserAuthenticationService> logger,
            IApplicationContext application,
            IMapper mapper,
            SecurityService securityService,
            CorpAttachmentService corpAttachmentService,
            ProjectService projectService
            )
        {
            this.userSevice = userSevice;
            this.corpService = corpService;
            this.managerService = managerService;
            this.constructorService = constructorService;
            this.httpContextAccessor = httpContextAccessor;
            this.applicationContext = applicationContext;
            this.configuration = configuration;
            this.commanderService = commanderService;
            this.memoryCache = memoryCache;
            this.logger = logger;
            this.application = application;
            this.mapper = mapper;
            this.securityService = securityService;
            this.corpAttachmentService = corpAttachmentService;
            this.projectService = projectService;
        }

        /// <summary>
        /// 建设单位信息完善
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        public LoginResult ConstructorSignIn(ConstructorEditForm form)
        {
            try
            {
                application.Transaction.Begin();
                string? code = memoryCache.Get<string>(form.EditToken);
                if (code == null)
                    throw new BusinessException($"建设单位{form.Code}登录信息不存在，请先登录后完善信息");
                if (code != form.Code)
                    throw new BusinessException($"建设单位{form.Code}编码和登录信息不一致");
                IUser? user = null;
                ConstructorInfo? constructorInfo = constructorService.GetConstructorInfo(code);
                if (constructorInfo == null)
                    throw new BusinessException($"建设单位{form.Code}登录信息不存在，请先登录后完善信息");
                ConstructorInfo updateForm = new ConstructorInfo();
                updateForm.HasAuth = true;
                updateForm.SecurityId = constructorInfo.SecurityId;
                updateForm.ProjectId = constructorInfo.ProjectId;
                mapper.Map(form, updateForm);
                constructorService.Update(updateForm);
                SysSecurity? sysSecurity = securityService.GetSysSecurity(constructorInfo.SecurityId);
                if (sysSecurity != null)
                {
                    sysSecurity.Password = SecurityPasswordHelper.EncodePassword(form.Password, UserPasswordFormat.SM4);
                    securityService.Update(sysSecurity);
                }
                user = constructorInfo;
                LoginResult result = CreateToken(user);
                application.Transaction.Complete();
                return result;
            }
            catch (Exception ex)
            {
                application.Transaction.Abort();
                logger.LogError($"建设单位信息完善失败:{form.Code}");
                throw new BusinessException($"建设单位信息完善失败:{form.Code}");
            }
        }

        /// <summary>
        /// 企业信息完善
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        public LoginResult CorpSignIn(CorpEditForm form)
        {
            try
            {
                application.Transaction.Begin();
                string? code = memoryCache.Get<string>(form.EditToken);
                if (code == null)
                    throw new BusinessException($"企业{form.Code}登录信息不存在，请先登录后完善信息");
                if (code != form.Code)
                    throw new BusinessException($"企业{form.Code}编码和登录信息不一致");
                IUser? user = null;
                CorpInfo? corpInfo = corpService.GetCorpInfo(code);
                if (corpInfo == null)
                    throw new BusinessException($"企业{form.Code}登录信息不存在，请先登录后完善信息");
                CorpInfo updateCorp = new CorpInfo();
                updateCorp.HasAuth = true;
                updateCorp.SecurityId = corpInfo.SecurityId;
                updateCorp.DataSource = corpInfo.DataSource;
                updateCorp.ProjectId = corpInfo.ProjectId;
                mapper.Map(form, updateCorp);
                corpService.Update(updateCorp);

                SysSecurity? sysSecurity = securityService.GetSysSecurity(corpInfo.SecurityId);
                if (sysSecurity != null)
                {
                    sysSecurity.Password = SecurityPasswordHelper.EncodePassword(form.Password, UserPasswordFormat.SM4);
                    securityService.Update(sysSecurity);
                }
                user = corpInfo;
                LoginResult result = CreateToken(user);

                #region 附件

                foreach (var item in form.Attachments)
                {
                    CorpAttachment projectAttachment = new()
                    {
                        Id = Guid.NewGuid().ToString().Replace("-", ""),
                        AttachmentId = item.AttachmentId,
                        ObjectId = corpInfo.CorpId.ToString(),
                        Tenant = 企业附件租户.Corp,
                        AttachmentType = 企业附件类型.营业执照,
                        CreateTime = DateTime.Now
                    };
                    corpAttachmentService.Create(projectAttachment);
                }

                #endregion 附件

                application.Transaction.Complete();
                return result;
            }
            catch (Exception ex)
            {
                application.Transaction.Abort();
                logger.LogError($"企业信息完善失败:{form.Code}");
                throw new BusinessException($"企业信息完善失败:{form.Code}");
            }
        }

        /// <summary>
        /// 创建登录信息
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public LoginResult CreateToken(IUser user)
        {
            LoginResult result = new();
            DateTime expires = DateTime.Now.AddMinutes(15);
            result.TokenType = JwtBearerDefaults.AuthenticationScheme;
            result.Expires = (long)expires.Subtract(DateTime.Now).TotalMilliseconds;
            result.AccessToken = GenerateJwtToken(user, expires);
            return result;
        }

        /// <summary>
        /// 登陆认证
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        public LoginResult SignIn(UserLoginForm form)
        {
            if (!form.UserType.HasValue)
                throw new BusinessException("登陆用户类型为空");

            IUser? user = null;
            //创建登陆结果
            LoginResult result = new();
            ProjectInfo? failProject;
            bool hsAuditProject = false;
            result.Type = form.UserType.Value;
            result.EditToken = Guid.NewGuid().ToString().Replace("-", "");
            result.ActionCode = LoginResultConstants.ActionCode.None;
            switch (form.UserType.Value)
            {
                case UserType.SysUser:
                    user = userSevice.PasswordSignIn(form.UserName, form.Password);
                    break;

                case UserType.Corp:
                    CorpInfo corpInfo = corpService.PasswordSignIn(form.UserName, form.Password) ?? throw new BusinessException($"登陆失败,用户类型:{form.UserType},用户名{form.UserName}");
                    if (corpInfo.CheckStatus != 审核状态.审核通过) throw new BusinessException($"该账户未审核通过，无法登录");
                    user = corpInfo;
                    if (corpInfo.HasAuth == false)
                    {
                        result.ActionCode = LoginResultConstants.ActionCode.CompleteInfo;
                        memoryCache.Set(result.EditToken, corpInfo.Code, DateTime.Now.AddDays(1));
                    }
                    else
                    {
                        projectService.HsAuditProject(user.Id, form.UserType.Value, out failProject);
                        if (failProject != null)
                            result.ActionCode = LoginResultConstants.ActionCode.RedirectProject;
                    }
                    break;

                case UserType.Manager:
                    user = managerService.PasswordSignIn(form.UserName, form.Password) ?? throw new BusinessException($"登陆失败,用户类型:{form.UserType},用户名{form.UserName}");

                    #region 判断当前劳资专管员下的项目，若有审核通过，无审核失败的直接登录,有审核通过，有审核失败需要跳转项目信息。无审核通过，有审核失败，直接跳转编辑

                    hsAuditProject = projectService.HsAuditProject(user.Id, form.UserType.Value, out failProject);
                    if (!hsAuditProject && failProject == null)
                        throw new BusinessException($"登陆失败,当前劳资专管员还没有审核完成的项目，请等待主管单位审核后登录");
                    if (!hsAuditProject && failProject != null)
                    {
                        result.ActionCode = LoginResultConstants.ActionCode.RedirectFailProject;
                        memoryCache.Set(result.EditToken, failProject.ProjectId + "_" + user.Id, DateTime.Now.AddDays(1));
                    }
                    if (hsAuditProject && failProject != null)
                        result.ActionCode = LoginResultConstants.ActionCode.RedirectProject;

                    #endregion 判断当前劳资专管员下的项目，若有审核通过，无审核失败的直接登录,有审核通过，有审核失败需要跳转项目信息。无审核通过，有审核失败，直接跳转编辑

                    break;

                case UserType.Constructor:
                    ConstructorInfo constructorInfo = constructorService.PasswordSignIn(form.UserName, form.Password) ?? throw new BusinessException($"登陆失败,用户类型:{form.UserType},用户名{form.UserName}");
                    user = constructorInfo;
                    if (constructorInfo.HasAuth == false)
                    {
                        result.ActionCode = LoginResultConstants.ActionCode.CompleteInfo;
                        memoryCache.Set(result.EditToken, constructorInfo.Code, DateTime.Now.AddDays(1));
                    }
                    break;

                case UserType.Commander:
                    user = commanderService.PasswordSignIn(form.UserName, form.Password);
                    break;

                default:
                    break;
            }

            //验证用户
            if (user == null)
                throw new BusinessException($"登陆失败,用户类型:{form.UserType},用户名{form.UserName}");
            if (result.ActionCode == LoginResultConstants.ActionCode.None)
            {
                result = CreateToken(user);
            }
            if (result.ActionCode == LoginResultConstants.ActionCode.RedirectProject)
            {
                result = CreateToken(user);
                result.ActionCode = LoginResultConstants.ActionCode.RedirectProject;
            }
            return result;
        }

        /// <summary>
        /// 获取登陆用户信息
        /// </summary>
        /// <returns></returns>
        public IUser? GetLoginUser()
        {
            var accessToken = httpContextAccessor.HttpContext?.GetTokenAsync("access_token").GetAwaiter().GetResult();
            if (string.IsNullOrEmpty(accessToken))
                return null;
            //if (!ValidateToken(accessToken))
            //    return null;
            var handler = new JwtSecurityTokenHandler();
            var decodedToken = handler.ReadJwtToken(accessToken);
            if (decodedToken == null || decodedToken.Claims.Count() == 0)
                return null;
            var objectIdClaim = decodedToken.Claims.FirstOrDefault(claim => claim.Type == "userId");
            var userTypeClaim = decodedToken.Claims.FirstOrDefault(claim => claim.Type == "role");
            if (objectIdClaim == null || userTypeClaim == null ||
                !int.TryParse(objectIdClaim.Value, out int objectId) ||
                !Enum.TryParse<UserType>(userTypeClaim.Value, true, out UserType userType))
                return null;

            switch (userType)
            {
                case UserType.SysUser:
                    return applicationContext.GetService<UserService>().GetUser(objectId);

                case UserType.Corp:
                    return applicationContext.GetService<CorpService>().GetCorpInfo(objectId);

                case UserType.Constructor:
                    return applicationContext.GetService<ConstructorService>().GetConstructorInfo(objectId);

                case UserType.Manager:
                    return applicationContext.GetService<ManagerService>().GetManagerInfo(objectId);

                case UserType.Commander:
                    return applicationContext.GetService<CommanderService>().GetCommanderInfo(objectId);

                default:
                    break;
            }
            return null;
        }

        /// <summary>
        /// 刷新签名
        /// </summary>
        /// <param name="user"></param>
        /// <param name="expires"></param>
        /// <returns></returns>
        public async Task<string> RefreshTokenAsync(IUser user, DateTime expires)
        {
            var accessToken = await httpContextAccessor.HttpContext?.GetTokenAsync("access_token");
            if (string.IsNullOrEmpty(accessToken))
                return string.Empty;

            var tokenHandler = new JwtSecurityTokenHandler();
            var secretKey = configuration.GetValue<string>("Jwt:SecretKey") ?? string.Empty;
            var key = Encoding.UTF8.GetBytes(secretKey);

            var validatedToken = tokenHandler.ValidateToken(accessToken, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = true,
                ValidIssuer = configuration.GetValue<string>("Jwt:Issuer", string.Empty), //发行人Issuer
                ValidAudience = configuration.GetValue<string>("Jwt:Audience", string.Empty), //订阅人Audience
                ValidateAudience = true,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero // 不允许时间偏差
            }, out SecurityToken validatedSecurityToken);

            var jwtToken = validatedSecurityToken as JwtSecurityToken;
            if (jwtToken != null && jwtToken.ValidTo < DateTime.Now.AddMinutes(8)) // 如果剩余有效时间小于5分钟，需要重新生成Token
            {
                return GenerateJwtToken(user, expires);
            }

            return accessToken; // 返回原Token
        }

        #region 私有方法

        //private bool ValidateToken(string token)
        //{
        //    var tokenHandler = new JwtSecurityTokenHandler();
        //    var secretKey = configuration.GetValue<string>("Jwt:SecretKey") ?? string.Empty;
        //    var key = Encoding.UTF8.GetBytes(secretKey);

        //    try
        //    {
        //        tokenHandler.ValidateToken(token, new TokenValidationParameters
        //        {
        //            ValidateIssuerSigningKey = true,
        //            IssuerSigningKey = new SymmetricSecurityKey(key),
        //            ValidateIssuer = true,
        //            ValidIssuer = configuration.GetValue<string>("Jwt:Issuer", string.Empty), //发行人Issuer
        //            ValidAudience = configuration.GetValue<string>("Jwt:Audience", string.Empty), //订阅人Audience
        //            ValidateAudience = true,
        //            ValidateLifetime = true,
        //            ClockSkew = TimeSpan.Zero // 不允许时间偏差
        //        }, out SecurityToken validatedToken);

        //        return true; // Token验证通过
        //    }
        //    catch
        //    {
        //        return false; // Token验证失败
        //    }
        //}

        /// <summary>
        /// 生成Jwt签名
        /// </summary>
        /// <param name="user"></param>
        /// <param name="expires"></param>
        /// <returns></returns>
        private string GenerateJwtToken(IUser user, DateTime expires)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var secretKey = configuration.GetValue<string>("Jwt:SecretKey") ?? string.Empty;
            var key = Encoding.UTF8.GetBytes(secretKey);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                new Claim("userId",user.Id.ToString()),
                new Claim("userName",user.Name),
                new Claim("role", user.Type.ToString()),
                }),
                Issuer = configuration.GetValue<string>("Jwt:Issuer", string.Empty), //发行人Issuer
                Audience = configuration.GetValue<string>("Jwt:Audience", string.Empty), //订阅人Audience
                Expires = expires, // Token过期时间为当前时间加上15分钟
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
            var jwtToken = tokenHandler.WriteToken(token);

            return jwtToken;
        }

        #endregion 私有方法
    }
}